home *** CD-ROM | disk | FTP | other *** search
/ Windows Expert / Windows Expert.iso / program / pchart.zip / PCHART.PAS < prev    next >
Pascal/Delphi Source File  |  1991-05-02  |  16KB  |  588 lines

  1. {**************************************************}
  2. {                    Chart 1.0                     }
  3. {                    Written in                    }
  4. {             Turbo Pascal for Windows             }
  5. {                Copyright (c) 1991                }
  6. {                  Zack Urlocker                   }
  7. {                    05/02/91                      }
  8. {**************************************************}
  9.  
  10. program PCharts;
  11.  
  12. { This is a simple implementation of a charting program written
  13.   in Turbo Pascal for Windows using the ObjectWindows application
  14.   framework.  The program is divided into several object types:
  15.  
  16.   TChartApplication      --creates and shows the main window
  17.   TChartDialog           --allows editing of data items
  18.   TChartWindow           --responds to Windows messages, menu commands,
  19.                            keyboard and mouse events
  20.   TChart and descendants --chart objects that can draw, rescale etc
  21.                            these are in the Charts unit
  22.   TDict and TAssoc       --data management objects
  23.                            these are in the Dicts unit
  24. }
  25.  
  26. {$R PChart.res}        { Link in resources }
  27.  
  28. {$IFDEF Final}        { Remove debug code for final version}
  29. {$D-,I-,L-,R-,S-}
  30. {$ELSE}
  31. {$D+,I+,L+,R+,S+}
  32. {$ENDIF}
  33.  
  34. uses Dicts, WObjects, WinTypes, WinProcs, Strings, StdDlgs, Charts;
  35.  
  36. const
  37.  cm_New    = 501;       { Menu items }
  38.  cm_Open   = 502;
  39.  cm_Save   = 503;
  40.  cm_SaveAs = 504;
  41.  cm_Exit   = 508;
  42.  cm_About  = 509;
  43.  cm_HBar   = 555;
  44.  cm_VBar   = 556;
  45.  cm_V3DBar = 557;
  46.  cm_Pie    = 558;
  47.  cm_Change = 552;
  48.  cm_SetName= 553;
  49.  cm_Help   = 600;
  50.  cm_CmdMode= 601;      { For Lotus style slash (/) key commands }
  51.  
  52.  id_Label  = 101;       { Dialog box fields}
  53.  id_Value  = 102;
  54.  id_Delete = 104;
  55.  fieldLen  = 16;
  56.  
  57. type
  58.  
  59.   { The application defines startup behavior for the window. }
  60.   TChartApplication = object(TApplication)
  61.     procedure InitInstance; virtual;
  62.     procedure InitMainWindow; virtual;
  63.   end;
  64.  
  65.   { Dialog transfer record }
  66.   ItemTransferBuffer = record
  67.     LabelStr, ValueStr : array[0..FieldLen-1] of char;
  68.   end;
  69.  
  70.   { The dialog is used for input of new data items. }
  71.   PChartDialog = ^TChartDialog;
  72.   TChartDialog = object(TDialog)
  73.     LabelEdit, valueEdit : PEdit;
  74.     constructor Init(AParent: PWindowsObject; ATitle:PChar);
  75.     procedure Delete(var Msg:TMessage); virtual id_First + id_Delete;
  76.   end;
  77.  
  78.   { The window responds to messages and controls the game board. }
  79.   PChartWindow = ^TChartWindow;
  80.   TChartWindow = object(TWindow)
  81.     Name : PChar;     { Name for file I/O     }
  82.     Chart : PChart;   { Pointer to a chartl   }
  83.     Saved : Boolean;  { has chart been saved? }
  84.     ItemBuffer : ItemTransferBuffer; { for ChartDialog }
  85.     constructor Init(AParent: PWindowsObject; ATitle: PChar);
  86.     procedure GetWindowClass(var WndClass: TWndClass); virtual;
  87.     procedure redraw;
  88.     function CanClose: Boolean; virtual;
  89.     procedure IOError(ErrMessage : PChar);
  90.     procedure SetCaption(FName : PChar);
  91.     function Read(fName : PChar): Boolean;
  92.     function Write(fName : PChar): Boolean;
  93.  
  94.     { menu response methods }
  95.     procedure NewFile(var Msg: TMessage); virtual cm_First + cm_New;
  96.     procedure Open(var Msg: TMessage); virtual cm_First + cm_Open;
  97.     procedure Save(var Msg: TMessage); virtual cm_First + cm_Save;
  98.     procedure SaveAs(var Msg: TMessage); virtual cm_First + cm_SaveAs;
  99.     procedure Exit(var Msg: TMessage); virtual cm_First + cm_Exit;
  100.     procedure HBar(var Msg: TMessage); virtual cm_First + cm_HBar;
  101.     procedure VBar(var Msg: TMessage); virtual cm_First + cm_VBar;
  102.     procedure V3DBar(var Msg: TMessage); virtual cm_First + cm_V3DBar;
  103.     procedure Pie(var Msg: TMessage); virtual cm_First + cm_Pie;
  104.     procedure Change(var Msg: TMessage); virtual cm_First + cm_Change;
  105.     procedure SetName(var Msg: TMessage); virtual cm_First + cm_SetName;
  106.     procedure About(var Msg: TMessage); virtual cm_First + cm_About;
  107.     procedure Help(var Msg: TMessage); virtual cm_First + cm_Help;
  108.     procedure CmdMode(var Msg: TMessage); virtual cm_First + cm_CmdMode;
  109.  
  110.     { windows message response methods }
  111.     procedure Paint(DC: HDC; var PaintInfo: TPaintStruct); virtual;
  112.     procedure wmSetFocus(var Msg: TMessage); virtual wm_SetFocus;
  113.     procedure wmKillFocus(var Msg: TMessage); virtual wm_KillFocus;
  114.     procedure wmLButtonDown(var Msg: TMessage); virtual wm_LButtonDown;
  115.     procedure wmKeyDown(var Msg: TMessage); virtual wm_KeyDown;
  116.     procedure wmSize(var Msg: TMessage); virtual wm_Size;
  117.   end;
  118.  
  119.  
  120. {--------------------------------------------------}
  121. { TChartApplication's method implementations:      }
  122. {--------------------------------------------------}
  123.  
  124. { Load the accelerator table for hotkeys }
  125. procedure TChartApplication.InitInstance;
  126. begin
  127.   Tapplication.InitInstance;
  128.   HAccTable := LoadAccelerators(HInstance, 'ChartKeys');
  129. end;
  130.  
  131. { Start the main window }
  132. procedure TChartApplication.InitMainWindow;
  133. begin
  134.   MainWindow := New(PChartWindow,
  135.                 Init(nil, 'PChart : (untitled)'));
  136. end;
  137.  
  138.  
  139. {--------------------------------------------------}
  140. { TChartDialog method implementations:             }
  141. {--------------------------------------------------}
  142.  
  143. { The edit controls will contain the transfer data. }
  144. constructor TChartDialog.Init(AParent: PWindowsObject; ATitle:PChar);
  145. begin
  146.   TDialog.Init(AParent, ATitle);
  147.   new(LabelEdit, initResource(@Self, id_Label, fieldLen));
  148.   new(ValueEdit, initResource(@Self, id_Value, fieldLen));
  149. end;
  150.  
  151. { Respond to Delete Button by transfering data out.
  152.   This is automatically done if the user presses Ok. }
  153. procedure TChartDialog.Delete(var Msg:TMessage);
  154. begin
  155.   TransferData(tf_GetData);
  156.   EndDlg(id_Delete);
  157. end;
  158.  
  159.  
  160. {--------------------------------------------------}
  161. { TChartWindow's method implementations:           }
  162. {--------------------------------------------------}
  163.  
  164. { Initialize all fields to starting values }
  165. constructor TChartWindow.Init(AParent: PWindowsObject; ATitle: PChar);
  166. var Msg : TMessage;
  167. begin
  168.   TWindow.Init(AParent, ATitle);
  169.   Chart := new(PVbarChart, init);
  170.   Saved := True;
  171.   getMem(Name, 255);
  172.   StrPcopy(ItemBuffer.LabelStr, 'Item');
  173.   StrPCopy(ItemBuffer.ValueStr, '50');
  174.   redraw;
  175.   with attr do
  176.   begin
  177.     w:=400;          { Force window size }
  178.     h:=300;
  179.   end;
  180. end;
  181.  
  182. { Override default cursor, icon, menu }
  183. procedure TChartWindow.GetWindowClass(var WndClass: TWndClass);
  184. begin
  185.   TWindow.GetWindowClass(WndClass);
  186.   WndClass.Style := 0;
  187.   WndClass.hCursor := LoadCursor(hInstance, 'ChartCur');
  188.   WndClass.hIcon := LoadIcon(hInstance, 'ChartIco');
  189.   WndClass.lpszMenuName := 'ChartMenu';
  190. end;
  191.  
  192. { Update the chart by rescaling, redrawing }
  193. procedure TChartWindow.redraw;
  194. begin
  195.   Chart^.area.x := attr.w;
  196.   Chart^.area.y := attr.h;
  197.   Chart^.reScale;
  198.   invalidateRect(HWindow, nil, True);
  199. end;
  200.  
  201. { Make sure the user has saved his work before closing }
  202. function TChartWindow.CanClose: Boolean;
  203. var Reply : Integer;
  204.     Msg : TMessage;
  205. begin
  206.   if not Saved then
  207.   begin
  208.     Reply := MessageBox(HWindow, 'File has not been saved. Save file before closing?',
  209.              'Warning', mb_IconStop or mb_YesNoCancel);
  210.     if Reply = id_Yes then
  211.       Save(Msg);
  212.   end;
  213.   CanClose := Saved or (Reply <> id_Cancel);
  214. end;
  215.  
  216. { Create a New chart }
  217. procedure TChartWindow.NewFile(var Msg: TMessage);
  218. begin
  219.   Chart := new(PVbarChart, init);
  220.   Saved := True;
  221.   StrDispose(Name);
  222.   GetMem(Name, 255);
  223.   setName(Msg);
  224.   StrPcopy(ItemBuffer.LabelStr, 'Item');
  225.   StrPCopy(ItemBuffer.ValueStr, '50');
  226.   redraw;
  227. end;
  228.  
  229. { Open a chart file }
  230. procedure TChartWindow.Open(var Msg: TMessage);
  231. var FName : PChar;
  232. begin
  233.   GetMem(FName, 255);
  234.   strPCopy(FName, '*.cht');
  235.   if application^.execDialog(New(PFileDialog,
  236.    init(@Self, PChar(sd_FileOpen), FName))) = ID_Ok then
  237.    begin
  238.      Chart := new(PChart, init);
  239.      StrCopy(Name, FName);
  240.      if Read(FName) then
  241.        redraw
  242.      else
  243.        newFile(Msg);
  244.    end;
  245.   Strdispose(FName);
  246. end;
  247.  
  248. { Save the chart with existing name.  Call SaveAs if necessary. }
  249. procedure TChartWindow.Save(var Msg: TMessage);
  250. begin
  251.   if strScan(Name, '.') = nil then
  252.     strCat(Name, '.cht');
  253.   if strLen(Name) > 4 then
  254.     write(Name)
  255.   else
  256.     SaveAs(Msg);
  257. end;
  258.  
  259. { Save the chart under a new name }
  260. procedure TChartWindow.SaveAs(var Msg: TMessage);
  261. var len : Integer;
  262.   OldName : PChar;  { in case user cancels command }
  263. begin
  264.   getMem(OldName, 255);
  265.   strCopy(OldName, Name);
  266.   { give a default name and extension }
  267.   if strLen(Name) = 0 then
  268.   begin
  269.     len := StrLen(Chart^.Name);
  270.     if len > 8 then len := 8;
  271.     StrLCopy(Name, Chart^.Name, len);
  272.   end;
  273.   if StrScan(Name, '.') = nil then
  274.     StrCat(Name, '.cht');
  275.   if StrLen(Name) < 5 then
  276.     StrPCopy(Name, 'Chart.cht');
  277.  
  278.   if application^.execDialog(New(PFileDialog,
  279.      init(@Self, PChar(sd_FileSave), Name))) = ID_Ok then
  280.        write(Name)
  281.   else
  282.        StrCopy(Name, OldName);
  283.   strDispose(OldName);
  284. end;
  285.  
  286. { Report an I/O Error }
  287. procedure TChartWindow.IOError(ErrMessage : PChar);
  288. var Msg : Array[0..255] of Char;
  289. begin
  290.   MessageBeep(0);
  291.   strCopy(Msg, ErrMessage);
  292.   MessageBox(0, StrCat(Msg, Name), 'File Error', mb_IconExclamation);
  293. end;
  294.  
  295. { Set the caption of the window to the filename }
  296. procedure TChartWindow.SetCaption(FName : PChar);
  297. var Caption : PChar;
  298. begin
  299.   getMem(Caption, 255);
  300.   strPCopy(Caption, 'PChart : ');
  301.   SetWindowText(Hwindow, strCat(Caption, FName));
  302.   strDispose(Caption);
  303. end;
  304.  
  305. { Read a chart from a file. }
  306. function TChartWindow.Read(FName : PChar) : Boolean;
  307. var S : TBufStream;
  308. begin
  309.   S.Init(FName, StOpenRead, 1024);
  310.   if S.Status <> stOk then
  311.     IOError('Can''t open file ')
  312.   else
  313.     begin
  314.       Chart := PChart(S.Get);
  315.       if S.Status <> stOk then
  316.     IOError('Can''t read file ')
  317.       else
  318.         setCaption(Name);
  319.     end;
  320.   S.Done;
  321.   Read := (S.Status = stOk);
  322. end;
  323.  
  324. { Store a chart onto a file by storing onto a stream. }
  325. function TChartWindow.Write(FName : PChar) : Boolean;
  326. var S : TBufStream;
  327. begin
  328.   S.Init(FName, stCreate, 1024);
  329.   if S.Status <> stOk then
  330.     IOError('Can''t create file ')
  331.   else
  332.     begin
  333.       S.put(Chart);
  334.       if S.Status <> stOk then
  335.     IOError('Can''t write file ')
  336.       else
  337.       begin
  338.         setCaption(Name);
  339.         Saved := True;
  340.       end;
  341.     end;
  342.   S.Done;
  343.   Write := (S.status = StOk);
  344. end;
  345.  
  346. { Make it a Horizontal Bar chart }
  347. procedure TChartWindow.HBar(var Msg: TMessage);
  348. Var Chart2 : PChart;
  349. begin
  350.   Chart2 := new(PHBarChart, init);
  351.   Chart2^.Items := Chart^.items;
  352.   Chart2^.Name := Chart^.Name;
  353.   Chart := PHBarChart(Chart2);
  354.   redraw;
  355. end;
  356.  
  357. { Make it a Vertical Bar chart }
  358. procedure TChartWindow.VBar(var Msg: TMessage);
  359. Var Chart2 : PChart;
  360. begin
  361.   Chart2 := new(PVBarChart, init);
  362.   Chart2^.Items := Chart^.items;
  363.   Chart2^.Name := Chart^.Name;
  364.   Chart := PVBarChart(Chart2);
  365.   redraw;
  366. end;
  367.  
  368. { Make it a Vertical Bar chart }
  369. procedure TChartWindow.V3DBar(var Msg: TMessage);
  370. Var Chart2 : PChart;
  371. begin
  372.   Chart2 := new(PV3DBarChart, init);
  373.   Chart2^.Items := Chart^.items;
  374.   Chart2^.Name := Chart^.Name;
  375.   Chart := PV3DBarChart(Chart2);
  376.   redraw;
  377. end;
  378.  
  379. { Make it a Pie chart }
  380. procedure TChartWindow.Pie(var Msg: TMessage);
  381. Var Chart2 : PChart;
  382. begin
  383.   Chart2 := new(PPieChart, init);
  384.   Chart2^.Items := Chart^.items;
  385.   Chart2^.Name := Chart^.Name;
  386.   Chart := PPieChart(Chart2);
  387.   redraw;
  388. end;
  389.  
  390. { Change, add or delete an item }
  391. procedure TChartWindow.Change(var Msg: TMessage);
  392. var  Dlg: TChartDialog;
  393.      Reply, Value, errorPos : Integer;
  394. begin
  395.   Dlg.Init(@Self, 'ChartDlg');
  396.   Dlg.TransferBuffer := @ItemBuffer;
  397.   Reply := Dlg.Execute;
  398.   Dlg.Done;
  399.   if Reply = id_Ok then
  400.   begin
  401.     { If valid, add the item to the chart }
  402.     val(ItemBuffer.ValueStr, value, errorPos);
  403.     if errorPos = 0 then
  404.     begin
  405.       if Chart = nil then
  406.         Chart := new(PVBarChart, init);
  407.       Chart^.add(ItemBuffer.LabelStr, Value);
  408.     end
  409.     else
  410.       MessageBeep(0);
  411.   end
  412.   else if Reply = id_Delete then
  413.      if Chart = nil then
  414.        MessageBeep(0)
  415.      else
  416.        Chart^.Remove(ItemBuffer.LabelStr);
  417.   { Adjust the chart }
  418.   if Reply <> id_Cancel then
  419.   begin
  420.     redraw;
  421.     Saved := False;
  422.   end;
  423. end;
  424.  
  425. { Set or change the name of the chart }
  426. procedure TChartWindow.SetName(var Msg: TMessage);
  427. var TempName : PChar;
  428. begin
  429.   GetMem(TempName, 40);
  430.   if Chart^.Name <> nil then
  431.     strLCopy(TempName, Chart^.Name, 40);
  432.   if application^.ExecDialog(New(PInputDialog,
  433.       Init(@Self, 'Chart', 'Enter chart name:',
  434.       TempName, 40))) = id_Ok then
  435.   begin
  436.      if chart^.Name <> nil then
  437.        strDispose(Chart^.Name);
  438.      getMem(Chart^.Name, 40);
  439.      strCopy(Chart^.Name, TempName);
  440.      redraw;
  441.   end;
  442.   strDispose(TempName);
  443. end;
  444.  
  445. { Display About box }
  446. procedure TChartWindow.About(var Msg: TMessage);
  447. var  Dlg: TDialog;
  448. begin
  449.   Dlg.Init(@Self, 'AboutDlg');
  450.   Dlg.Execute;
  451.   Dlg.Done;
  452. end;
  453.  
  454. { Display Help dialog }
  455. procedure TChartWindow.Help(var Msg: TMessage);
  456. var  Dlg: TDialog;
  457. begin
  458.   Dlg.Init(@Self, 'HelpDlg');
  459.   Dlg.Execute;
  460.   Dlg.Done;
  461. end;
  462.  
  463. { Respond to Lotus style commands from slash (/) accelerator }
  464. procedure TChartWindow.CmdMode(var Msg: TMessage);
  465. begin
  466.   sendMessage(HWindow, WM_SYSCOMMAND, $F100, 0);
  467. end;
  468.  
  469. { Exit the program }
  470. procedure TChartWindow.Exit(var Msg: TMessage);
  471. begin
  472.   if CanClose then postQuitMessage(0);
  473. end;
  474.  
  475. { Draw the chart if it exists }
  476. procedure TChartWindow.Paint(DC: HDC; var PaintInfo: TPaintStruct);
  477. var s : array[0..16] of Char;
  478. begin
  479.   if Chart <> nil then
  480.     Chart^.draw(DC)
  481.   else
  482.   begin
  483.     strPCopy(s, 'Error: No chart');
  484.     TextOut(DC, 10, 10, s, strLen(s));
  485.   end;
  486. end;
  487.  
  488. { Ensure that cursor is visible even when no mouse }
  489. procedure TChartWindow.wmSetFocus(var Msg: TMessage);
  490. begin
  491.   ShowCursor(True);
  492. end;
  493.  
  494. { Return cursor to previous state for other windows }
  495. procedure TChartWindow.wmKillFocus(var Msg: TMessage);
  496. begin
  497.   ShowCursor(False);
  498. end;
  499.  
  500. { Select and item in the chart and edit it }
  501. procedure TChartWindow.wmLButtonDown(var Msg: TMessage);
  502. var Item : PAssoc;
  503.     S : String;
  504. begin
  505. { First locate the item clicked on }
  506.   Item := Chart^.getItem(Msg.LParamLo, Msg.LParamHi);
  507.   if Item <> nil then
  508.   begin
  509.     { Update the edit buffer and edit }
  510.     strLCopy(ItemBuffer.LabelStr, Item^.key, fieldLen-1);
  511.     str(Item^.value,S);
  512.     strPCopy(ItemBuffer.ValueStr, S);
  513.     Change(Msg);
  514.   end
  515.   else
  516.     MessageBeep(0);
  517. end;
  518.  
  519. { Simulate mouse movement with cursor keys }
  520. procedure TChartWindow.wmKeyDown(var Msg: TMessage);
  521. var x, y : Integer;
  522.     pos : TPoint;
  523.     key : word;
  524. begin
  525.   { Determine position of cursor in Window }
  526.   getCursorPos(pos);
  527.   screenToClient(HWindow, pos);
  528.   x:=pos.x;
  529.   y:=pos.y;
  530.   { move the cursor position }
  531.   key := Msg.WParam;
  532.   case key of
  533.     VK_UP    : y := y - 10;
  534.     VK_DOWN  : y := y + 10;
  535.     VK_RIGHT : x := x + 10;
  536.     VK_LEFT  : x := x - 10;
  537.     VK_HOME  :
  538.       begin
  539.     x := 10;
  540.     y := 10;
  541.       end;
  542.     VK_END :
  543.       begin
  544.     x := attr.w - 10;
  545.     y := attr.h - 10;
  546.       end;
  547.     VK_RETURN,
  548.     VK_SPACE,
  549.     VK_F2:
  550.       begin
  551.         { Simulate mouse pressing at cursor position }
  552.         Msg.LParam := LongInt(pos);
  553.     wmLButtonDown(Msg);
  554.       end;
  555.     end;
  556.     { Update position of cursor in window with clipping }
  557.     if x < 1 then x := 10;
  558.     if y < 1 then y := 10;
  559.     if x >= attr.w then x:= attr.w - 10;
  560.     if y >= attr.h then y:= attr.h - 10;
  561.     pos.x := x;
  562.     pos.y := y;
  563.     clientToScreen(HWindow, pos);
  564.     setCursorPos(pos.x, pos.y);
  565. end;
  566.  
  567. { update internal information when resizing then redraw }
  568. procedure TChartWindow.wmSize(var Msg: TMessage);
  569. begin
  570.   attr.h := Msg.lParamHi;
  571.   attr.w := Msg.lParamLo;
  572.   redraw
  573. end;
  574.  
  575.  
  576. {--------------------------------------------------}
  577. { Main program:                                    }
  578. {--------------------------------------------------}
  579.  
  580. var
  581.   ChartApp: TChartApplication;
  582.  
  583. begin
  584.   ChartApp.Init('PChart');
  585.   ChartApp.Run;
  586.   ChartApp.Done;
  587. end.
  588.